﻿using System;
using System.Globalization;

namespace Nomo.CoreModule
{
    /// <summary>
    /// 表示一个 0 到 1 之间的小数。
    /// </summary>
    [Serializable]
    public readonly struct Normal : IComparable, IComparable<Normal>, IEquatable<Normal>, IFormattable
    {
        private readonly byte _value;

        /// <summary>
        /// 表示 <see cref="Normal"/> 结构所能表示的最小值。
        /// </summary>
        public static readonly Normal MinValue = new Normal(byte.MinValue);

        /// <summary>
        /// 表示 <see cref="Normal"/> 结构所能表示的最大值。
        /// </summary>
        public static readonly Normal MaxValue = new Normal(byte.MaxValue);

        /// <summary>
        /// 使用指定的单精度浮点数初始化 <see cref="Normal"/> 结构的新实例。
        /// </summary>
        /// <param name="value">一个单精度浮点数。</param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> 为 <see cref="float.NaN"/></exception>
        public Normal(float value)
        {
            if (float.IsNaN(value))
            {
                throw new ArgumentOutOfRangeException(nameof(value));
            }
            if (value < 0F)
            {
                value = 0F;
            }
            else if (value > 1F)
            {
                value = 1F;
            }
            _value = (byte) (value * byte.MaxValue);
        }

        /// <summary>
        /// 使用指定的双精度浮点数初始化 <see cref="Normal"/> 结构的新实例。
        /// </summary>
        /// <param name="value">一个双精度浮点数。</param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> 为 <see cref="double.NaN"/></exception>
        public Normal(double value)
        {
            if (double.IsNaN(value))
            {
                throw new ArgumentOutOfRangeException(nameof(value));
            }
            if (value < 0D)
            {
                value = 0D;
            }
            else if (value > 1D)
            {
                value = 1D;
            }
            _value = (byte) (value * byte.MaxValue);
        }

        private Normal(byte value)
        {
            _value = value;
        }

        /// <inheritdoc />
        public int CompareTo(object obj)
        {
            if (obj is Normal normal)
            {
                return CompareTo(normal);
            }
            if (obj != null)
            {
                throw new ArgumentException();
            }
            return 1;
        }

        /// <inheritdoc />
        public int CompareTo(Normal other)
        {
            if (this < other)
            {
                return -1;
            }
            if (this > other)
            {
                return 1;
            }
            return 0;
        }

        /// <inheritdoc />
        public bool Equals(Normal other)
        {
            return this == other;
        }

        /// <inheritdoc />
        public override bool Equals(object obj)
        {
            return obj is Normal normal && Equals(normal);
        }

        /// <inheritdoc />
        public override int GetHashCode()
        {
            return _value.GetHashCode();
        }

        /// <inheritdoc />
        public string ToString(string format, IFormatProvider formatProvider)
        {
            return ((double) this).ToString(format, formatProvider);
        }

        /// <inheritdoc />
        public override string ToString()
        {
            return ((double) this).ToString(null, NumberFormatInfo.CurrentInfo);
        }

        /// <summary>
        /// 返回一个值，该值指示指定的 <see cref="Normal"/> 值是否等于另一个指定的 <see cref="Normal"/> 值。
        /// </summary>
        /// <param name="left">要比较的第一个值。</param>
        /// <param name="right">要比较的第二个值。</param>
        /// <returns><paramref name="left"/> 是否等于 <paramref name="right"/> .</returns>
        public static bool operator ==(Normal left, Normal right)
        {
            return left._value == right._value;
        }

        /// <summary>
        /// 返回一个值，该值指示指定的 <see cref="Normal"/> 值是否不等于另一个指定的 <see cref="Normal"/> 值。
        /// </summary>
        /// <param name="left">要比较的第一个值。</param>
        /// <param name="right">要比较的第二个值。</param>
        /// <returns><paramref name="left"/> 是否不等于 <paramref name="right"/> .</returns>
        public static bool operator !=(Normal left, Normal right)
        {
            return left._value != right._value;
        }

        /// <summary>
        /// 返回一个值，该值指示指定的 <see cref="Normal"/> 值是否小于另一个指定的 <see cref="Normal"/> 值。
        /// </summary>
        /// <param name="left">要比较的第一个值。</param>
        /// <param name="right">要比较的第二个值。</param>
        /// <returns><paramref name="left"/> 是否小于 <paramref name="right"/> .</returns>
        public static bool operator <(Normal left, Normal right)
        {
            return left._value < right._value;
        }

        /// <summary>
        /// 返回一个值，该值指示指定的 <see cref="Normal"/> 值是否大于另一个指定的 <see cref="Normal"/> 值。
        /// </summary>
        /// <param name="left">要比较的第一个值。</param>
        /// <param name="right">要比较的第二个值。</param>
        /// <returns><paramref name="left"/> 是否大于 <paramref name="right"/> .</returns>
        public static bool operator >(Normal left, Normal right)
        {
            return left._value > right._value;
        }

        /// <summary>
        /// 返回一个值，该值指示指定的 <see cref="Normal"/> 值是否小于或等于另一个指定的 <see cref="Normal"/> 值。
        /// </summary>
        /// <param name="left">要比较的第一个值。</param>
        /// <param name="right">要比较的第二个值。</param>
        /// <returns><paramref name="left"/> 是否小于或等于 <paramref name="right"/> .</returns>
        public static bool operator <=(Normal left, Normal right)
        {
            return left._value <= right._value;
        }

        /// <summary>
        /// 返回一个值，该值指示指定的 <see cref="Normal"/> 值是否大于或等于另一个指定的 <see cref="Normal"/> 值。
        /// </summary>
        /// <param name="left">要比较的第一个值。</param>
        /// <param name="right">要比较的第二个值。</param>
        /// <returns><paramref name="left"/> 是否大于或等于 <paramref name="right"/> .</returns>
        public static bool operator >=(Normal left, Normal right)
        {
            return left._value >= right._value;
        }

        /// <summary>
        /// 返回指定的 <see cref="Normal"/> 值与另一个指定的 <see cref="Normal"/> 值相加的结果。
        /// </summary>
        /// <param name="left">参与计算的第一个值。</param>
        /// <param name="right">参与计算的第二个值。</param>
        /// <returns><paramref name="left"/> 与 <paramref name="right"/> 相加的结果。</returns>
        public static Normal operator +(Normal left, Normal right)
        {
            var value = left._value + right._value;
            return value > byte.MaxValue ? MaxValue : new Normal((byte) value);
        }

        /// <summary>
        /// 返回指定的 <see cref="Normal"/> 值与另一个指定的 <see cref="Normal"/> 值相减的结果。
        /// </summary>
        /// <param name="left">参与计算的第一个值。</param>
        /// <param name="right">参与计算的第二个值。</param>
        /// <returns><paramref name="left"/> 与 <paramref name="right"/> 相减的结果。</returns>
        public static Normal operator -(Normal left, Normal right)
        {
            var value = left._value - right._value;
            return value < byte.MinValue ? MinValue : new Normal((byte) value);
        }

        /// <summary>
        /// 返回指定的 <see cref="Normal"/> 值与另一个指定的 <see cref="Normal"/> 值相乘的结果。
        /// </summary>
        /// <param name="left">参与计算的第一个值。</param>
        /// <param name="right">参与计算的第二个值。</param>
        /// <returns><paramref name="left"/> 与 <paramref name="right"/> 相乘的结果。</returns>
        public static Normal operator *(Normal left, Normal right)
        {
            return (Normal) ((double) left * right);
        }

        /// <summary>
        /// 返回指定的 <see cref="Normal"/> 值与另一个指定的 <see cref="Normal"/> 值相除的结果。
        /// </summary>
        /// <param name="left">参与计算的第一个值。</param>
        /// <param name="right">参与计算的第二个值。</param>
        /// <returns><paramref name="left"/> 与 <paramref name="right"/> 相除的结果。</returns>
        public static Normal operator /(Normal left, Normal right)
        {
            return (Normal) ((double) left / right);
        }

        /// <summary>
        /// 返回指定的 <see cref="Normal"/> 值与另一个指定的 <see cref="Normal"/> 值相模的结果。
        /// </summary>
        /// <param name="left">参与计算的第一个值。</param>
        /// <param name="right">参与计算的第二个值。</param>
        /// <returns><paramref name="left"/> 与 <paramref name="right"/> 相模的结果。</returns>
        public static Normal operator %(Normal left, Normal right)
        {
            return (Normal) ((double) left % right);
        }

        /// <summary>
        /// 将指定的 <see cref="Single"/> 值转换为 <see cref="Normal"/> 值。
        /// </summary>
        /// <param name="value">要进行转换的数。</param>
        /// <returns>从 <paramref name="value"/> 转换而来的 <see cref="Normal"/> 值。</returns>
        public static explicit operator Normal(float value)
        {
            return new Normal(value);
        }

        /// <summary>
        /// 将指定的 <see cref="Normal"/> 值转换为 <see cref="Single"/> 值。
        /// </summary>
        /// <param name="value">要进行转换的数。</param>
        /// <returns>从 <paramref name="value"/> 转换而来的 <see cref="Single"/> 值。</returns>
        public static implicit operator float(Normal value)
        {
            return (float) ((double) value._value / byte.MaxValue);
        }

        /// <summary>
        /// 将指定的 <see cref="Double"/> 值转换为 <see cref="Normal"/> 值。
        /// </summary>
        /// <param name="value">要进行转换的数。</param>
        /// <returns>从 <paramref name="value"/> 转换而来的 <see cref="Normal"/> 值。</returns>
        public static explicit operator Normal(double value)
        {
            return new Normal(value);
        }

        /// <summary>
        /// 将指定的 <see cref="Normal"/> 值转换为 <see cref="Double"/> 值。
        /// </summary>
        /// <param name="value">要进行转换的数。</param>
        /// <returns>从 <paramref name="value"/> 转换而来的 <see cref="Double"/> 值。</returns>
        public static implicit operator double(Normal value)
        {
            return (double) value._value / byte.MaxValue;
        }
    }
}
